package com.hightrast.service.api.baseEntity;

import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Optional;

@ApiModel(
    description = "返回信息"
)
public class R<T> implements Serializable {
    private static final long serialVersionUID = 1L;
    @ApiModelProperty(
        value = "状态码",
        required = true
    )
    private int code;
    @ApiModelProperty(
        value = "是否成功",
        required = true
    )
    private boolean success;
    @ApiModelProperty("承载数据")
    @JsonFormat(
        with = {JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY}
    )
    private T data;
    @ApiModelProperty(
        value = "返回消息",
        required = true
    )
    private String msg;
    @ApiModelProperty(
        value = "链路日志",
        required = true
    )
    private String traceLog;

    private R(IResultCode resultCode) {
        this(resultCode, (T) null, resultCode.getMessage());
    }

    private R(IResultCode resultCode, String msg) {
        this(resultCode, (T) null, msg);
    }

    private R(IResultCode resultCode, T data) {
        this(resultCode, data, resultCode.getMessage());
    }

    private R(IResultCode resultCode, T data, String msg) {
        this(resultCode.getCode(), data, msg);
    }

    private R(int code, T data, String msg) {
        this.code = code;
        this.data = data;
        this.msg = msg;
        this.success = ResultCode.SUCCESS.code == code;
    }

    public static boolean isSuccess(R<?> result) {
        return (Boolean) Optional.ofNullable(result).map((x) -> {
            return nullSafeEquals(ResultCode.SUCCESS.code, x.code);
        }).orElse(Boolean.FALSE);
    }

    public static boolean nullSafeEquals(Object o1, Object o2) {
        if (o1 == o2) {
            return true;
        } else if (o1 != null && o2 != null) {
            if (o1.equals(o2)) {
                return true;
            } else {
                return o1.getClass().isArray() && o2.getClass().isArray() ? arrayEquals(o1, o2) : false;
            }
        } else {
            return false;
        }
    }

    private static boolean arrayEquals(Object o1, Object o2) {
        if (o1 instanceof Object[] && o2 instanceof Object[]) {
            return Arrays.equals((Object[])((Object[])((Object[])o1)), (Object[])((Object[])((Object[])o2)));
        } else if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
            return Arrays.equals((boolean[])((boolean[])((boolean[])o1)), (boolean[])((boolean[])((boolean[])o2)));
        } else if (o1 instanceof byte[] && o2 instanceof byte[]) {
            return Arrays.equals((byte[])((byte[])((byte[])o1)), (byte[])((byte[])((byte[])o2)));
        } else if (o1 instanceof char[] && o2 instanceof char[]) {
            return Arrays.equals((char[])((char[])((char[])o1)), (char[])((char[])((char[])o2)));
        } else if (o1 instanceof double[] && o2 instanceof double[]) {
            return Arrays.equals((double[])((double[])((double[])o1)), (double[])((double[])((double[])o2)));
        } else if (o1 instanceof float[] && o2 instanceof float[]) {
            return Arrays.equals((float[])((float[])((float[])o1)), (float[])((float[])((float[])o2)));
        } else if (o1 instanceof int[] && o2 instanceof int[]) {
            return Arrays.equals((int[])((int[])((int[])o1)), (int[])((int[])((int[])o2)));
        } else if (o1 instanceof long[] && o2 instanceof long[]) {
            return Arrays.equals((long[])((long[])((long[])o1)), (long[])((long[])((long[])o2)));
        } else {
            return o1 instanceof short[] && o2 instanceof short[] ? Arrays.equals((short[])((short[])((short[])o1)), (short[])((short[])((short[])o2))) : false;
        }
    }

    public static boolean isNotSuccess(R<?> result) {
        return !isSuccess(result);
    }

    public static <T> R<T> data(T data) {
        return data(data, "Actions successful");
    }

    public static <T> R<T> data(T data, String msg) {
        return data(200, data, msg);
    }

    public static <T> R<T> data(int code, T data, String msg) {
        // 暂无数据
        return new R(code, data, data == null ? "No data" : msg);
    }

    public static <T> R<T> success() {
        return success("Actions successful");
    }

    public static <T> R<T> success(String msg) {
        return new R(ResultCode.SUCCESS, msg);
    }

    public static <T> R<T> success(IResultCode resultCode) {
        return new R(resultCode);
    }

    public static <T> R<T> success(IResultCode resultCode, String msg) {
        return new R(resultCode, msg);
    }

    public static <T> R<T> fail(String msg) {
        return new R(ResultCode.FAILURE, msg);
    }

    public static <T> R<T> fail(int code, String msg) {
        return new R(code, (Object)null, msg);
    }

    public static <T> R<T> fail(IResultCode resultCode) {
        return new R(resultCode);
    }

    public static <T> R<T> fail(IResultCode resultCode, String msg) {
        return new R(resultCode, msg);
    }

    public static <T> R<T> fail(String msg, T data) {
        return new R(ResultCode.FAILURE, data, msg);
    }

    public static <T> R<T> status(boolean flag) {
        return flag ? success("Actions successful") : fail("Actions failed");
    }

    public int getCode() {
        return this.code;
    }

    public boolean isSuccess() {
        return this.success;
    }

    public T getData() {
        return this.data;
    }

    public String getMsg() {
        return this.msg;
    }

    public String getTraceLog() {
        return this.traceLog;
    }

    public void setCode(final int code) {
        this.code = code;
    }

    public void setSuccess(final boolean success) {
        this.success = success;
    }

    public void setData(final T data) {
        this.data = data;
    }

    public void setMsg(final String msg) {
        this.msg = msg;
    }

    public void setTraceLog(final String traceLog) {
        this.traceLog = traceLog;
    }

    public String toString() {
        return "R(code=" + this.getCode() + ", success=" + this.isSuccess() + ", data=" + this.getData() + ", msg=" + this.getMsg() + ", traceLog=" + this.getTraceLog() + ")";
    }

    public R() {
    }
}